package com.zyk.scaffold.user.service.impl;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zyk.scaffold.common.enums.FunctionEnum;
import com.zyk.scaffold.common.utils.BeanCopyUtil;
import com.zyk.scaffold.common.utils.StringUtils;
import com.zyk.scaffold.user.config.NacosConfigListenerConfiguration;
import com.zyk.scaffold.user.domain.FunctionApisData;
import com.zyk.scaffold.user.entity.Function;
import com.zyk.scaffold.user.entity.FunctionApi;
import com.zyk.scaffold.user.mapper.FunctionApiMapper;
import com.zyk.scaffold.user.service.FunctionApiService;
import com.zyk.scaffold.user.service.FunctionService;
import com.google.common.base.Functions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户-功能api关系表 服务实现类
 * </p>
 *
 * @author zhouyk
 * @since 2022-01-24
 */
@Slf4j
@Service
public class FunctionApiServiceImpl extends ServiceImpl<FunctionApiMapper, FunctionApi> implements FunctionApiService {

    @Autowired
    private FunctionService functionService;

    @Autowired
    NacosConfigManager nacosConfigManager;

    @Override
    public Boolean refreshFunctionApi(Long userId) {
        String group = nacosConfigManager.getNacosConfigProperties().getGroup();
        ConfigService configService = nacosConfigManager.getConfigService();
        String config = null;
        try {
            config = configService.getConfig(NacosConfigListenerConfiguration.NACOS_CONFIG_DATAID, group, 5000L);
        } catch (NacosException e) {
            log.error("pull config from nacos error, data-id:backend-scaffold-function-api, group:" + group, e);
        }
        return refreshFunctionApi(config, userId);
    }

    @Override
    public Boolean refreshFunctionApi(String config, Long userId) {
        List<FunctionApisData> functionApisData = new ArrayList<>();
        if(StringUtils.isNotEmpty(config) && JSONValidator.from(config).validate()){
            functionApisData = JSONObject.parseArray(config, FunctionApisData.class);
        }

        if(CollectionUtils.isEmpty(functionApisData)){
            return false;
        }

        functionApisData = flatTree(functionApisData);

        Map<String, FunctionApisData> mapCode2FunctionApisData = functionApisData.stream().collect(Collectors.toMap(FunctionApisData::getCode, Functions.identity()));

        List<Function> allFunction = functionService.getAllFunction();
        Map<String, Function> mapCode2Function = allFunction.stream().collect(Collectors.toMap(Function::getCode, Functions.identity()));

        Set<String> deltedCodes = new HashSet<>(mapCode2Function.keySet());
        deltedCodes.removeAll(mapCode2FunctionApisData.keySet());
        ArrayList<Long> deletedIds = new ArrayList<>();
        ArrayList<Function> adds = new ArrayList<>();
        ArrayList<Function> updates = new ArrayList<>();

        mapCode2FunctionApisData.keySet().forEach(code -> {
            Function function = mapCode2Function.get(code);
            FunctionApisData functionApisDataTmp = mapCode2FunctionApisData.get(code);
            if(function != null){
                if(!deltedCodes.contains(code)){
                    updates.add(mergeContent(function, functionApisDataTmp, userId));
                }
            }else {
                adds.add(wrapperFunctionDomain(functionApisDataTmp, userId));
            }
        });
        deltedCodes.forEach(item ->{
            deletedIds.add(mapCode2Function.get(item).getId());
        });
        functionService.refreshFunction(adds, updates, deletedIds, userId);

        this.lambdaUpdate().in(FunctionApi::getDeleted, Boolean.TRUE).remove();
        // 删除所有关系
        UpdateWrapper<FunctionApi> functionApiUpdateWrapper = new UpdateWrapper<>();
        functionApiUpdateWrapper.lambda().in(FunctionApi::getDeleted, Boolean.FALSE)
                .set(FunctionApi::getUpdateUser, userId)
                .set(FunctionApi::getDeleted, Boolean.TRUE);
        this.update(functionApiUpdateWrapper);

        allFunction.addAll(adds);

        ArrayList<FunctionApi> functionApis = new ArrayList<>();
        for (Function function : allFunction) {
            String code = function.getCode();
            FunctionApisData functionApisDataTmp = mapCode2FunctionApisData.get(code);
            if(functionApisDataTmp != null && !CollectionUtils.isEmpty(functionApisDataTmp.getApis())){
                functionApis.addAll(wrapperFunctionPermissionDomain(functionApisDataTmp, function, userId));
            }
        }
        this.saveBatch(functionApis);
        return true;
    }

    private List<FunctionApisData> flatTree(List<FunctionApisData> functionApisData) {
        List<FunctionApisData> functionApisDatas = new ArrayList<>();
        for (FunctionApisData functionApisDatum : functionApisData) {
            if (StringUtils.isEmpty(functionApisDatum.getType())){
                functionApisDatum.setType(FunctionEnum.MENU.getCode());
            }
            functionApisDatas.add(functionApisDatum);
            List<FunctionApisData> childrens = functionApisDatum.getChildrens();
            if(!CollectionUtils.isEmpty(childrens)){
                List<FunctionApisData> functionApisDataChildrens = flatTree(childrens);
                functionApisDataChildrens.forEach(item -> item.setParentCode(functionApisDatum.getCode()));
                functionApisDatum.setChildrens(null);
                functionApisDatas.addAll(functionApisDataChildrens);
            }
        }
        return functionApisDatas;
    }

    private List<FunctionApi> wrapperFunctionPermissionDomain(FunctionApisData functionApisData, Function functionDomain, Long userId){
        List<FunctionApi> functionApis = new ArrayList<>();
        functionApisData.getApis().forEach(value ->{
            FunctionApi functionApi = new FunctionApi();
            functionApi.setFunctionId(functionDomain.getId());
            functionApi.setApiUrl(value.getApiUrl());
            functionApi.setApiDes(value.getApiDes());
            functionApi.setDeleted(Boolean.FALSE);
            functionApi.setCreateUser(userId);
            functionApi.setUpdateUser(userId);
            functionApis.add(functionApi);
        });
        return functionApis;
    }

    private Function mergeContent(Function source, FunctionApisData tmp, Long userId) {
        source.setName(tmp.getName());
        source.setType(tmp.getType());
        source.setDeleted(Boolean.FALSE);
        source.setUpdateUser(userId);
        return source;
    }
    private Function wrapperFunctionDomain(FunctionApisData functionPermissionConfigMeta, Long userId) {
        Function function = BeanCopyUtil.copyProperties(functionPermissionConfigMeta, Function::new);
        function.setDeleted(Boolean.FALSE);
        function.setCreateUser(userId);
        function.setUpdateUser(userId);
        return function;
    }

    @Override
    public List<FunctionApi> getFunctionApisByFunctionIds(List<Long> functionIds) {
        if(CollectionUtils.isEmpty(functionIds)){
            return Collections.emptyList();
        }
        QueryWrapper<FunctionApi> functionApiQueryWrapper = new QueryWrapper<>();
        functionApiQueryWrapper.lambda()
                .in(FunctionApi::getFunctionId, functionIds)
                .eq(FunctionApi::getDeleted, Boolean.FALSE);
        return this.list(functionApiQueryWrapper);
    }
}
